package collect

import (
	"bufio"
	"io"
	"os"
	"regexp"
	"strconv"
	//"strings"
	//"fmt"
	// "errors"
	"log"
	"time"
)

const TIME_FORMAT = "2006-01-02 15:04:05"

/*
125.119.84.63 - - [16/Oct/2017:14:43:18 +0800] "GET /css/laydate/laydate.js HTTP/2.0" 304 87 "https://hp.yidianling.com/crm-client/depart" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
*/
type Item struct {
	Ip        string
	Time      string
	Method    string
	Url       string
	Status    int
	BodySize  int
	Proto     string
	Referer   string
	UserAgent string
	Host      string
}

type ItemCallback func(*Item)

type Collect struct {
	filePath     string
	preg         string
	needUnzip    bool
	itemCallback ItemCallback
	wg           WaitGroupWrap
	chanPool     chan bool
	chanItem     chan *Item
	f            *os.File
}

func NewCollect(filePath string, needUnzip bool) *Collect {
	c := &Collect{}

	c.init(filePath, needUnzip)
	return c
}

func (c *Collect) init(filePath string, needUnzip bool) {
	c.filePath = filePath
	c.needUnzip = needUnzip
	c.chanPool = make(chan bool, 100)
	c.chanItem = make(chan *Item, 100)

}

func (c *Collect) GetItems() error {
	filePath := c.filePath
	if c.needUnzip {
		unZip(c.filePath)
		filePath = dstFile(c.filePath)
	}

	f, err := os.Open(filePath)
	if err != nil {
		return err
	}
	c.f = f
	rd := bufio.NewReader(c.f)
	c.wg.Wrap(func() {

		for {
			line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
			if err != nil && io.EOF != err {
				break
			}

			if err != nil && io.EOF == err {
				break
			}

			c.wg.Wrap(func() {
				c.GetItem(line)
			})

		}

	})
	c.wg.Wrap(func() {
		for {
			select {
			case item := <-c.chanItem:
				c.chanPool <- true //缓冲区满之后阻塞，后面的readChan将等待
				c.wg.Wrap(func() {
					c.handleItem(item)
					<-c.chanPool
				})
			case <-time.After(30 * time.Second):
				log.Printf("timed out") //等待30后退出
				return
			}
		}
	})
	return nil

}

func (c *Collect) GetItem(line string) {
	itemR := regexp.MustCompile("(.*?)\\s.*?\\s.*?\\s\\[(.*?)\\]\\s\"(.*?)\\s(.*?)\\s(.*?)\"\\s(\\d+?)\\s(\\d+?)\\s\"(.*?)\"\\s\"(.*?)\"\\s\"(.*?)\"")
	matches := itemR.FindStringSubmatch(line)

	if len(matches) == 0 {

		return
	}

	status, _ := strconv.Atoi(matches[6])
	bodySize, _ := strconv.Atoi(matches[7])

	t1, _ := time.Parse(
		TIME_FORMAT,
		NginxTimeFormat(matches[2]))

	item := &Item{
		Ip:        matches[1],
		Time:      t1.String(),
		Method:    matches[3],
		Url:       matches[4],
		Status:    status,
		BodySize:  bodySize,
		Proto:     matches[5],
		Referer:   matches[8],
		UserAgent: matches[9],
		Host:      matches[10],
	}

	c.chanItem <- item
}

func (c *Collect) OnItem(callback ItemCallback) {
	c.itemCallback = callback
}

func (c *Collect) handleItem(item *Item) {

	c.itemCallback(item)
}

func (c *Collect) Exit() {

	c.wg.Wait()

	if c.f != nil {
		c.f.Close()
		c.f = nil
	}
}
